import Checkbox from "../inner/Checkbox";
import type { TreeContextProps} from ".";
import { dragHoverPartEnum, TreeContextKey } from ".";
import type { TreeNode } from "./store";
import Loading from "../inner/Loading";
import { FeatherChevronRight } from "cui-vue-icons/feather";
import { computed, defineComponent, inject, PropType, ref, watchEffect } from "vue";


export default defineComponent({
    name: 'Node',
    props: {
        data: {type: Object as PropType<TreeNode>, required: true}
    },
    setup (props, {emit}) {
        const ctx: TreeContextProps = inject(TreeContextKey);
        const nodeBody = ref();
        const dragoverBefore = ref(false);
        const dragoverBody = ref(false);
        const dragoverAfter = ref(false);

        const opened = () => ctx.store.store.nodeMap[props.data[ctx.store.keyField]]?.expand;
        const selected = computed(() => ctx.store.store.nodeMap[props.data[ctx.store.keyField]]?._selected);
        const dragging = computed(() => ctx.store.store.nodeMap[props.data[ctx.store.keyField]]?._dragging);
        const hasChildren = ref(props.data.children && props.data.children.length || props.data.loading);

        const classList = () => ({
            'cm-tree-item': true,
            'cm-tree-item-open': opened(),
            'cm-tree-item-selected': selected.value,
            'cm-tree-item-dragging': dragging.value,
            'cm-tree-item-leaf': !hasChildren.value,
            [`${ctx.draggingClass}`]: !!ctx.draggingClass && dragging.value,
            [`${ctx.selectedClass}`]: ctx.selectedClass && selected.value,
        });

        watchEffect(() => {
            hasChildren.value = props.data.children && props.data.children.length || props.data.loading;
        });

        const icon = computed(() => {
            let icon = ctx.directory ? (hasChildren.value ? <span class="cm-tree-item-folder" />
                : <span class="cm-tree-item-file" />) : null;
            if (ctx.customIcon && typeof ctx.customIcon === 'function') {
                icon = <span class="cm-tree-item-icon">{ctx.customIcon(props.data)}</span>;
            }
            if (props.data.icon) {
                icon = <span class="cm-tree-item-icon">{props.data.icon}</span>;
            }
            return icon;
        });

        const onContextMenu = () => {
            if (ctx && ctx.contextMenu) {
                ctx.onContextMenu && ctx.onContextMenu({...props.data});
            }
        };

        const onOpenClose = () => {
            ctx.onOpenClose(props.data);
        };

        const onNodeSelect = () => {
            ctx.onNodeSelect(props.data);
        };

        const onDragStart = (e: any) => {
            if (props.data.disabled) {
                return;
            }
            if (ctx.draggable) {
                if (e.dataTransfer) {
                    e.dataTransfer.setData('node', props.data[ctx.store.keyField]);
                }
                // 打开的父节点进行关闭
                if (opened()){
                    ctx.onOpenClose(props.data);
                }
                ctx.store.setNodeDragging(props.data[ctx.store.keyField], true);
                ctx.onDragStart?.(e, props.data);
            }
        };

        const onDragEnd = (e: any) => {
            if (ctx?.store) {
                ctx.store.setNodeDragging(props.data[ctx.store.keyField], false);
            }
        };

        const getHoverPart = (e: any) => {
            const boundingClientRect = nodeBody.value.getBoundingClientRect();
            const height = boundingClientRect.height;
            const y = e.clientY - boundingClientRect.top;
            if (y <= height * 0.3) return dragHoverPartEnum.before;
            if (y <= height * (0.3 + 0.4)) return dragHoverPartEnum.body;
            return dragHoverPartEnum.after;
        };

        const onDragEnter = (e: any) => {
            e.preventDefault();
            const hoverPart = getHoverPart(e);
            resetDragoverFlags(hoverPart);
            ctx.onDragEnter?.(e, props.data, hoverPart);
        };

        const onDragOver = (e: any) => {
            e.preventDefault();
            const hoverPart = getHoverPart(e);
            resetDragoverFlags(hoverPart);
            ctx.onDragOver?.(e, props.data, hoverPart);
        };

        const onDragLeave = (e: any) => {
            const hoverPart = getHoverPart(e);
            resetDragoverFlags(hoverPart, true);
            ctx.onDragLeave?.(e, props.data, hoverPart);
        };

        const onDrop = (e: any) => {
            const hoverPart = getHoverPart(e);
            resetDragoverFlags(hoverPart, true);
            ctx.onDrop?.(e, props.data[ctx.store.keyField], hoverPart);
        };


        const resetDragoverFlags = (hoverPart: dragHoverPartEnum, isLeaveOrDrop = false) => {
            dragoverBefore.value = false;
            dragoverBody.value = false;
            dragoverAfter.value = false;
            if (!isLeaveOrDrop) {
                if (hoverPart === dragHoverPartEnum.before) dragoverBefore.value = true;
                else if (hoverPart === dragHoverPartEnum.body) dragoverBody.value = true;
                else if (hoverPart === dragHoverPartEnum.after) {
                    isLast() && (dragoverAfter.value = true);
                }
            }
        };

        const isLast = () => {
            const parent = props.data._parent;
            if (parent) {
                const index = parent.children.findIndex((item: TreeNode) => item[ctx.store.keyField] === props.data[ctx.store.keyField]);
                if (index === parent.children.length - 1) return true;
            }
            return false;
        };

        const paddingStyle = () => ({'padding-left': 16 * props.data._level + 'px', height: '100%'});
        const beforeClassList = computed(() => ({'cm-tree-node-drop': true, 'cm-tree-node-drop_active': dragoverBefore.value}));
        const afterClassList = computed(() => ({'cm-tree-node-drop': true, 'cm-tree-node-drop_active': dragoverAfter.value}));
        const bodyClassList = computed(() => ({'cm-tree-node-content': true, 'cm-tree-node-drop_active': dragoverBody.value, [`${ctx.dragHoverClass}`]: !!ctx.dragHoverClass && dragoverBody.value}));

        const loading = computed(() => ctx.store.store.nodeMap[props.data[ctx.store.keyField]]?.__loading);

        return () => <div class={classList()} style={paddingStyle()} onDragenter={onDragEnter}
            onDragover={onDragOver} onDragleave={onDragLeave} onDrop={onDrop} ref={nodeBody}>
            <div class={beforeClassList.value} />
            <div class={bodyClassList.value} onDragstart={onDragStart} onDragend={onDragEnd} draggable={ctx.draggable && !props.data.disabled}>
                {
                    loading.value
                        ? <Loading color={'#1890ff'} size={16}/>
                        : <span class="cm-tree-arrow" onClick={onOpenClose}>
                            {
                                ctx.arrowIcon && typeof ctx.arrowIcon
                                    ? ctx.arrowIcon?.()
                                    : <FeatherChevronRight />
                            }
                        </span>
                }
                {
                    ctx.checkable
                        ? <Checkbox disabled={ctx.store.store.nodeMap[props.data[ctx.store.keyField]].disabled}
                            checked={ctx.store.store.nodeMap[props.data[ctx.store.keyField]].checked} onChange={(checked) => ctx.onNodeCheck(props.data, checked)}/>
                        : null
                }
                {icon.value}
                <span onContextmenu={onContextMenu} class={`cm-tree-title`} onClick={onNodeSelect}>
                    <span class="cm-tree-text">{props.data.title}</span>
                    {props.data.patch ? <span class="cm-tree-patch">{props.data.patch}</span> : null}
                </span>
            </div>
            <div class={afterClassList.value} />
        </div>;
    }
});
